#include <openssl/evp.h>
#include <openssl/core_names.h>
#include <openssl/err.h>
#include <openssl/param_build.h>
#include <openssl/x509.h>

#include "print.h"

enum {
    FAIL = -1,
    OK = 0,
};

int main()
{
    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_SM2, NULL);
    if (!ctx) {
        LOGE("ctx NULL %s", ERR_reason_error_string(ERR_get_error()));
        return FAIL;
    }
    int ret = EVP_PKEY_keygen_init(ctx);
    if (ret != 1) {
        LOGE("EVP_PKEY_keygen_init ret = %d %s", ret, ERR_reason_error_string(ERR_get_error()));
        return FAIL;
    }
    EVP_PKEY *pkey = NULL;

    ret = EVP_PKEY_keygen(ctx, &pkey);
    if (ret != 1) {
        LOGE("EVP_PKEY_keygen ret = %d %s", ret, ERR_reason_error_string(ERR_get_error()));
        return FAIL;
    }

    int len = i2d_PUBKEY(pkey, NULL);
    uint8_t *derPubKey = NULL;
    if (len < 0 ) {
        LOGE("");
        return FAIL;
    }
    derPubKey = malloc(len);
    if (derPubKey == NULL) {
        LOGE("NULL");
        return FAIL;
    }
    ret = i2d_PUBKEY(pkey, &derPubKey);
    if (ret != len) {
        LOGE("");
        return FAIL;
    }
    PrintBuffer("derPubKey", derPubKey, len);

    OSSL_PARAM *params = NULL;
    ret = EVP_PKEY_todata(pkey, EVP_PKEY_PUBLIC_KEY, &params);
    if (ret != 1) {
        LOGE("EVP_PKEY_todata ret = %d", ret);
        return FAIL;
    }
    LOGI("EVP_PKEY_todata ok!");
    LOGI("params->key = %s", params->key);
    LOGI("params->data_type = %u", params->data_type);
    LOGI("params->data_size = %zu", params->data_size);
    LOGI("params->return_size = 0x%zx", params->return_size);
    PrintBuffer("params->data", params->data, params->data_size);
    OSSL_PARAM *pubKeyParam = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
    if (pubKeyParam == NULL) {
        LOGE("OSSL_PARAM_locate NULL %s", ERR_reason_error_string(ERR_get_error()));
        return FAIL;
    }
    LOGI("pubKeyParam->key = %s", pubKeyParam->key);
    LOGI("pubKeyParam->data_type = %u", pubKeyParam->data_type);
    LOGI("pubKeyParam->data_size = %zu", pubKeyParam->data_size);
    LOGI("pubKeyParam->return_size = 0x%zx", pubKeyParam->return_size);
    PrintBuffer("pubKeyParam->data", pubKeyParam->data, pubKeyParam->data_size);

    return OK;
}